﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Drawing;
using System.Drawing.Imaging;
using AForge.Imaging.Filters;
using AForge;
using AForge.Imaging;

using Image = System.Drawing.Image;

namespace PdfLib
{
    /// <summary>
    /// transform image and run edge detection to 
    /// get lines and rectangles
    /// </summary>
    public class ImageProcesser
    {
        #region fields
        private Bitmap _Img;
        private List<Rectangle> _Rects;
        private List<HorizontalLine> _HLines;
        private List<VerticalLine> _VLines;
        #endregion

        #region ctor
        /// <summary>
        /// ctor
        /// </summary>
        /// <param name="img"></param>
        /// <param name="pageSize"></param>
        public ImageProcesser(Bitmap img, SizeF pageSize)
        {
            this._Img = img;
            this._Img = this.TransformPixelFormat(this._Img);
            this._Img = this.EnlargeGrayScale(this._Img);
            Rectangle[] rects = this.FindRectanglesFromImage(this._Img);
            this._Rects = this.FilterRectangles(new Rectangle(0, 0, this._Img.Width, this._Img.Height), rects);
            float scaleX = pageSize.Width / this._Img.Width;
            float scaleY = pageSize.Height / this._Img.Height;
            this._Rects = this.ApplyScaleFactors(this._Rects, scaleX, scaleY);
        }
        #endregion

        #region lines
        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        public List<VerticalLine> ReadVerticalLines()
        {
            // TODO: read v lines from image
            return this._VLines;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        public List<HorizontalLine> ReadHorizontalLines()
        {
            // TODO: read h lines from image
            return this._HLines;
        }
        #endregion

        #region rects
        /// <summary>
        /// results of blob detection from bitmap
        /// </summary>
        /// <returns></returns>
        public List<Rectangle> ReadRectangles()
        {
            return this._Rects;
        }
        #endregion

        #region img processing
        private Bitmap TransformPixelFormat(Bitmap img)
        {
            if (img.PixelFormat != PixelFormat.Format8bppIndexed)
            {
                GrayscaleBT709 filter = new GrayscaleBT709();
                img = filter.Apply(img);
            }
            return img;
        }

        private Bitmap EnlargeGrayScale(Bitmap img)
        {
            LevelsLinear filter = new LevelsLinear();
            IntRange inRange = new IntRange(235, 255);
            IntRange outRange = new IntRange(0, 255);
            filter.InGray = inRange;
            filter.OutGray = outRange;
            img = filter.Apply(img);
            return img;
        }

        private Rectangle[] FindRectanglesFromImage(Bitmap img)
        {
            BlobCounter blobCounter = new BlobCounter(img);
            Rectangle[] rects = blobCounter.GetObjectRectangles();
            return rects;
        }
        #endregion

        #region filter
        private List<Rectangle> FilterRectangles(Rectangle pageRect, Rectangle[] rects)
        {
            List<Rectangle> filteredRects = new List<Rectangle>();

            if (rects != null && rects.Length > 0)
            {
                foreach (Rectangle rect in rects)
                {
                    if ((rect.Width > 100 && rect.Height >= 20) || (rect.Width >= 20 && rect.Height > 100))
                    {
                        filteredRects.Add(rect);
                        if (rect.X == 0 && rect.Y == 0)
                        {
                            if (rect.Width > pageRect.Width && rect.Height > pageRect.Height)
                            {
                                pageRect = rect;
                            }
                        }
                    }
                }
            }

            List<Rectangle> rectAdded = new List<Rectangle>();
            foreach (Rectangle rect in filteredRects)
            {
                if (!this.HasLeftCell(rect, filteredRects, pageRect))
                {
                    Rectangle leftCell = this.CreateLeftCell(rect, pageRect);
                    rectAdded.Add(leftCell);
                }
                else if (!this.HasRightCell(rect, filteredRects, pageRect))
                {
                    Rectangle rightCell = this.CreateRightCell(rect, pageRect);
                    rectAdded.Add(rightCell);
                }
            }
            if (rectAdded.Count > 0)
            {
                filteredRects.AddRange(rectAdded);
            }

            // remove page rect and empty rect
            List<Rectangle> rectToDraw = new List<Rectangle>();
            foreach (Rectangle rect in filteredRects)
            {
                if (rect.Width == pageRect.Width && rect.Height == pageRect.Height)
                {
                } // filteredRects.Remove(rect);
                else if (rect.Width <= 20 || rect.Height <= 20)
                {
                } // filteredRects.Remove(rect);
                else
                    rectToDraw.Add(rect);
            }

            return rectToDraw;
        }

        private void FilterLines()
        {
        }
        #endregion

        #region layout
        private bool HasLeftCell(Rectangle currentRect, List<Rectangle> allRects, Rectangle pageRect)
        {
            bool hasLeftCell = false;
            foreach (Rectangle rect in allRects)
            {
                if (rect.Width < pageRect.Width && rect.Height < pageRect.Height && rect.X > 0 && rect.Y > 0)
                {
                    if (rect.Y + rect.Height > currentRect.Y && rect.Y < currentRect.Y + currentRect.Height)
                    {
                        if (rect.X + rect.Width < currentRect.X + 20)
                        {
                            hasLeftCell = true;
                            break;
                        }
                    }
                }
            }
            return hasLeftCell;
        }

        private bool HasRightCell(Rectangle currentRect, List<Rectangle> allRects, Rectangle pageRect)
        {
            bool hasRightCell = false;
            foreach (Rectangle rect in allRects)
            {
                if (rect.Width < pageRect.Width && rect.Height < pageRect.Height && rect.X > 0 && rect.Y > 0)
                {
                    if (rect.Y + rect.Height > currentRect.Y && rect.Y < currentRect.Y + currentRect.Height)
                    {
                        if (rect.X + 20 > currentRect.X + currentRect.Width)
                        {
                            hasRightCell = true;
                            break;
                        }
                    }
                }
            }
            return hasRightCell;
        }

        private Rectangle CreateLeftCell(Rectangle currentRect, Rectangle pageRect)
        {
            Rectangle leftCell = new Rectangle(pageRect.X, currentRect.Y, currentRect.X - pageRect.X, currentRect.Height);
            return leftCell;
        }

        private Rectangle CreateRightCell(Rectangle currentRect, Rectangle pageRect)
        {
            Rectangle rightCell = new Rectangle(currentRect.X + currentRect.Width, currentRect.Y, pageRect.X + pageRect.Width - currentRect.X - currentRect.Width, currentRect.Height);
            return rightCell;
        }
        #endregion

        #region scale
        private List<Rectangle> ApplyScaleFactors(List<Rectangle> rects, float scaleX, float scaleY)
        {
            List<Rectangle> adjustedRects = new List<Rectangle>();
            foreach (Rectangle rect in rects)
            {
                RectangleF newRect = new RectangleF(
                    rect.X * scaleX,
                    rect.Y * scaleY,
                    rect.Width * scaleX,
                    rect.Height * scaleY);
                adjustedRects.Add(
                    new Rectangle(
                        (int) newRect.X,
                        (int) newRect.Y,
                        (int) newRect.Width,
                        (int) newRect.Height));
            }
            return adjustedRects;
        }
        #endregion
    }
}
